#ifndef __M_SERVICE_H__
#define __M_SERVICE_H__

#include "data.hpp"
#include"config.hpp"
#include"hot.hpp"
#include"util.hpp"
#include "httplib.h"
// 因为业务处理的回调函数没有传入参数的地方，因此无法直接访问外部的数据管理模块数据
// 可以使用lamda表达式解决，但是所有的业务功能都要在一个函数内实现，于功能划分上模块不够清晰
// 因此将数据管理模块的对象定义为全局数据,在这里声明一下，就可以在任意位置访问了
extern vic_cloud::DataManager *_data;
namespace vic_cloud
{
    class Service
    {
    public:
        Service()
        {
            Config *config = Config ::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
        }
        // 服务启动（httplib 绑定对应处理函数）
        bool RunModule()
        {
            // 正则表达式 ：
            //   .    匹配除\n和\r之外的任何单个字符
            //   .*  匹配任意一个字符任意次
            _server.Post("/upload",UpLoad); 
          _server.Get("/download/(.*)",DownLoad);
          _server.Get("/listshow",ListShow);                                                                                                                                                 
          _server.Get("/",ListShow);
          _server.listen("0.0.0.0",_server_port);//启动服务器

        }

    private:
        // 注意回调函数都需要设置成static，因为httplib库中函数要求的参数只有两个
        // 如果不用static修饰，那么会多出来一个this指针

        // 上传文件
        static void UpLoad(const httplib::Request &req, httplib::Response &res)
        {
            // post/upload  文件数据在正文中（正文并不全是文件数据）
            auto ret = req.has_file("file"); // 判断有没有上传文件区域
            if (ret == false)
            {
                res.status = 400;
                return;
            }
            
            const auto &file = req.get_file_value("file");
            // file.filename//文件名称   file.content//文件数据
            std::string back_dir = Config::GetInstance()->GetBackDir();
            std::string realpath = back_dir + FileUtil(file.filename).FileName();
            FileUtil fu(realpath);
            fu.SetContent(file.content); // 将数据写入文件中
            BackupInfo info;
            info.NewBackupInfo(realpath); // 备份，组织备份的文件信息
            _data->Insert(info);          // 向数据管理模块添加备份的文件信息
            std::stringstream ss; 
            
            return;
        }
        // 文件列表展示
        static std::string TimetoStr(time_t t)
        {
            std::string tmp = std::ctime(&t);
            return tmp;
        }
        static void ListShow(const httplib::Request &req, httplib::Response &res)
        {
            std::cout<<"listshow"<<std::endl;
            //1.获取所有文件的备份信息
                std::vector<BackupInfo> arry;
                _data->GetAll(&arry);
                //2.根据所有备份信息，组织html文件数据
                std::stringstream ss; 
                ss<<"<!DOCTYPE html>"<<"<html><meta charset=\"UTF-8\"><head><title>Download</title></head>";
                 ss<<"<body><h1>Download<br>victor's cloud</h1><table>";
                 for(auto &a:arry)
                 {
                     ss<<"<tr>";
                     std::string filename=FileUtil(a._real_path).FileName();
                     ss<<"<td><a href='"<<a._url_path<<"'>"<<filename<<"</a></td>";
                     ss<<"<td alight='right'>"<<TimetoStr(a._mtime)<<"</td>";
                     ss<<"<td alight='right'>"<<a._fsize/1024<<"k</td>";
                     ss<<"</tr>";
                 }
                 ss<<"</table></body></html>";
                 ss<<"<form action=\"http://49.233.202.241:8080/upload\" method=\"post\" enctype=\"multipart/form-data\"><div><input type=\"file\" name=\"file\"></div><input type=\"submit\" value=\"上传\"></div></form>";
                 res.body=ss.str();
                 res.set_header("Content-Type","text/html");
                 res.status=200;
                 return ;

        }
        
        // 文件下载
        static std::string GetETag(const BackupInfo &info)
        {
            // etg:filename-fsize-mtime
            FileUtil fu(info._real_path);
            std::string etag = fu.FileName();
            etag += "-";
            etag += std::to_string(info._fsize);
            etag += "-";
            etag += std::to_string(info._mtime);
            return etag;
        }

        static void DownLoad(const httplib::Request &req, httplib::Response &res)
        {
            // 1. 获取客户端请求的资源路径path  req.path
            // 2. 根据资源路径，获取文件的备份信息
            BackupInfo info;
            _data->GetOneByURL(req.path, &info);
            // 3. a:判断文件是否被压缩，如果被压缩，先进行解压
            if (info._pack_flag == true)
            {
                FileUtil fu(info._pack_path);
                fu.UnCompress(info._real_path); // 将文件解压到备份目录下
                //   b:删除压缩包，修改备份信息（文件已经没有被压缩）
                fu.Remove();
                info._pack_flag = false;
                _data->Update(info);
            }
            // 4. 读取文件数据，并将数据写入rsp.body中
            FileUtil fu(info._real_path);
            fu.GetContent(&res.body);
            // 判断是否断点续传
            bool retrans = false; // 符合断电续传的标志位
            std::string old_etag;
            if (req.has_file("If_Range"))
            {
                // 有if-range字段&&etag相同 ：符合断点续传
                old_etag = req.get_header_value("If_Range");
                if (old_etag == GetETag(info))
                {
                    retrans = true;
                }
            }
            if (retrans == false)
            {
                // 没有If-Rang字段||etag值不一样  则是正常下载（重新返回全部数据）

                // 5. 设置响应头部字段：ETag,Accept-Ranges:bytes
                res.set_header("Accept-Ranges", "bytes");
                res.set_header("ETag", GetETag(info));
                res.set_header("Content-Type", "application/octet-stream"); // application/octet-stream——表示的是我响应的正文是一个二进制数据流（常用于文件下载）
                res.status = 200;
            }
            else
            {
                // httplib已近支持力断点续传
                //只需要我们用户将文件所有数据取到res.body中，它内部会自动根据请求区间，从body中取出指定区间数据进行响应
                fu.GetContent(&res.body);
                res.set_header("Accept-Ranges", "bytes");
                res.set_header("ETag", GetETag(info));
                res.status = 206;
            }
        }

    private:
        int _server_port;             // 端口号
        std::string _server_ip;       // 服务器ip
        std::string _download_prefix; // 下载路径前缀
        httplib::Server _server;      // httplib库搭建服务器
    };

}
#endif
